﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using MiniFox.Components;
using MiniFox.Configurations;
using MiniFox.Log;
using MiniFox.Log.Configurations;
using MiniFox.Utility;
using MiniFox.Web.Infrastructure;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace MiniFox.Web.Middlewares
{
    /// <summary>
    /// 
    /// </summary>
    public class WebWatcherMiddleware : MiddlewareBase
    {
        Stopwatch _stopwatch;
        /// <summary>
        /// 
        /// </summary>
        public WebWatcherMiddleware()
        {
            _stopwatch = new Stopwatch();
        }

        private Stopwatch Stopwatch
        {
            get { return this._stopwatch; }
        }

        LoggerConfiguration _configuration;
        /// <summary>
        /// 
        /// </summary>
        protected LoggerConfiguration Configuration
        {
            get
            {
                if (_configuration == null)
                {
                    _configuration = ConfigurationFactory.GetConfig<LoggerConfiguration>();
                }
                return _configuration;
            }
        }

        private bool RequireLogForContentType(string contentType)
        {
            bool require = false;
            if (Configuration != null)
            {
                string[] values = Configuration.WebContentFilters;
                if (values != null)
                    foreach (string val in values)
                    {
                        require |= (contentType != null && contentType.IndexOf(val, StringComparison.OrdinalIgnoreCase) >= 0);
                        if (require)
                            break;
                    }
            }
            return require;

        }

        private bool NoLogPath(HttpRequest request)
        {
            string path = request.Path;
            bool exception = false; // 假定非例外
            if (Configuration.ExceptionUrls != null)
            {
                foreach (string url in Configuration.ExceptionUrls)
                {
                    
                    if (string.Equals(url, path, StringComparison.OrdinalIgnoreCase) || Regex.IsMatch(path, url))
                    {
                        exception = true;
                        break;
                    }
                }
            }
            return exception;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        protected override void BeforeInvoke(MiddlewareContext context)
        {
            HttpContext httpContext = context.CurrentHttpContext;
            HttpRequest request = httpContext.Request;
            if (NoLogPath(request))
                return;

            this.Stopwatch.Start();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        protected override void AfterInvoke(MiddlewareContext context)
        {
            var httpContext = context.CurrentHttpContext;

            if (this.Stopwatch.IsRunning)
                this.Stopwatch.Stop();
            if (NoLogPath(httpContext.Request) || !RequireLogForContentType(httpContext.Response.ContentType))
                return;

            var factory = ComponentFactory.GetComponent<LoggerFactory>();
            ILogger logger = factory.CreateLogger(WebExtensions.LOGGER_NAME);
            var logitem = CreateLogContent(httpContext, null);
            logger.WriteInfo(logitem);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="exception"></param>
        /// <returns></returns>
        protected async override Task InvokeError(HttpContext httpContext, Exception exception)
        {
            if (this.Stopwatch.IsRunning)
                this.Stopwatch.Stop();
            var logitem = CreateLogContent(httpContext, exception);
            await base.InvokeError(httpContext, exception);
            var factory = ComponentFactory.GetComponent<LoggerFactory>();
            ILogger logger = factory.CreateLogger(WebExtensions.LOGGER_NAME);
            logger.WriteError(logitem);
        }

        private LogWebContent CreateLogContent(HttpContext context, Exception exception)
        {
            long elapsed = this.Stopwatch.ElapsedMilliseconds;
            var logContent = new LogWebContent
            {
                Host = context.Request.GetFullHost(),
                ClientIP = context.Request.GetClientIP(),
                HttpMethod = context.Request.Method,
                RawUrl = context.Request.GetDisplayUrl(),
                StatusCode = context.Response.StatusCode,
                Elapsed = elapsed,
                User = context.User == null || context.User.Identity == null ? string.Empty : context.User.Identity.Name,
                RequestMessage = context.Request.GetHttpBodyContent(),
                RequestHeaders = context.Request.Headers.ToJsonString(),
                ResponseHeaders = context.Response.Headers.ToJsonString(),
                RequestCookies = context.Request.Cookies.ToJsonString(),
                ResponseCookies = context.Response.Cookies.ToJsonString(),
                Exception = exception
            };
            return logContent;
        }
    }
}
